﻿//Select the use of Instance Controller.
// Defining it value causes the code to be included in your compile otherwise it is excluded.
// This appears in two location in this application: here below and in the WebRole.cs file

#undef USEINSTANCECONTROL
//#define USEINSTANCECONTROL


using System;
using System.Diagnostics;
using System.Linq;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.Diagnostics;
using Microsoft.WindowsAzure.ServiceRuntime;
using Microsoft.WindowsAzure.StorageClient;
using WADConfigureWebRole;

namespace MyFloridaHouse.MapServer
{
	public class Global : System.Web.HttpApplication
	{
		//public static string SettingsFileName = Properties.Settings.Default.AppSettingXmlFileName;
		//private const string AppSettingsXmlTag = "applicationSettings";

#if (USEINSTANCECONTROL)
		/// <summary>
		/// Globally accessible object that is necessary for instance control. As a static variable it
		/// lives throughout the life of the application.
		/// </summary>
		public static InstanceController.ConfigurationDAL.ConfigurationDataModel ConfigurationDataModel;
#endif
		#region private field initializations
		private static ApplicationSettings _appSettings = new ApplicationSettings();
		#endregion


		private static object _gate = new object();
		private static bool _initialized = false;

		private bool _enableInstanceControlInitialization;

		#region Remove this code after the next .NET 4.0 service pack
		protected void Application_BeginRequest()
		{
			// See discussion in Application_Start method
			if (_initialized) return;

			lock (_gate)
			{
				if (_initialized) return;

				#region Setup CloudStorageAccount Configuration and Azure Diagnostics

				if (RoleEnvironment.IsAvailable)
				{
					// You must configure Azure to use table storage if you plan to use tables in your application
					// Setting enableInstanceControlInitialization causes this code to skip the initialization of the
					// Instance controller.
					_enableInstanceControlInitialization = true;
					StartStorageAccess(_enableInstanceControlInitialization);	//comment out: If you are NOT using table storage AND NOT using instance control.

#if (USEINSTANCECONTROL)
					// Custom diagnostics configuration as of Azure SDK 1.3
					// The PlugIn may be customized for specific needs.
					PlugIn.ConfigureAzureDiagnostics();
#endif
				}

				#endregion

				_initialized = true;
			}
		}
		#endregion

		#region application and session methods
		void Application_Start(object sender, EventArgs e)
		{
			// There is a know issue with .NET 4.0 in IIS 7 Full-trust mode and consequently in Azure when calling environment methods
			// in the Application_Start method.  In Azure it is customary to configure Role settings in the Application_Start method.
			// Because there is a Response context (not null) .NET 4.0 throws an error.  This will be fixed per Microsoft in the next
			// service pack.

			// the workaround is to not call Application_Start instead use Application_BeginRequest method.  This induces a small amount
			// of overhead since it is invoked on each request.

			// Uncomment the code after the next .NET sp release.
			#region Setup CloudStorageAccount Configuration and Azure Diagnostics

			//if (RoleEnvironment.IsAvailable)
			//{
			//    // You must configure Azure to use table storage if you plan to use tables in your application
			//    // Setting enableInstanceControlInitialization causes this code to skip the initialization of the
			//    // Instance controller.
			//    _enableInstanceControlInitialization = true;
			//    StartStorageAccess(_enableInstanceControlInitialization);	//comment out: If you are NOT using table storage AND NOT using instance control.

			//    // Custom diagnostics configuration as of Azure SDK 1.3
			//    // The PlugIn may be customized for specific needs.
			//    PlugIn.ConfigureAzureDiagnostics();
			//}

			#endregion
		}

		void Application_Error(object sender, EventArgs e)
		{
			// Code that runs when an unhandled error occurs

		}

		void Session_Start(object sender, EventArgs e)
		{
			// Code that runs when a new session is started

		}

		void Session_End(object sender, EventArgs e)
		{
			// Code that runs when a session ends. 
			// Note: The Session_End event is raised only when the sessionstate mode
			// is set to InProc in the Web.config file. If session mode is set to StateServer 
			// or SQLServer, the event is not raised.

		}
		#endregion

		/// <summary>
		/// Starts Azure Table Storage access.
		/// The method wraps up a sequance of actions that must happen to allow the entire
		/// application to work with table storage.
		/// </summary>
		private static void StartStorageAccess(bool enable)
		{
			//Initialize your Table Storage tables and connections (if any) using the values in the xml file
			//some settins may be overridden by the configuration stored in table storage.
			_appSettings = ApplicationSettingsManager.DynamicObjectMethods.GetServiceSourcePropertyValues(
				new ApplicationSettings(),
				new ApplicationSettingsManager.Manager().AppSettings);		// loads the static ApplicationSettings object.

			ConfigureTableStorageAccess();									// Required for cloud storage.

			// Adding the listener here instead of the Web.config because the Azure trace listener will cause
			// problems when running in IIS, so loading it here will ensure it is only active when running in Azure.
			Trace.Listeners.Add(new DiagnosticMonitorTraceListener());

			//Initialize your Table Storage tables and connections (if any)
			SetupApplicationTableStorage();									// Define your table storage connections 


#if (USEINSTANCECONTROL)
			//Initialize your diagnostics and instance control
			if (!enable) return;
			//Initialize the Instance Controller if it is being implemented in your application.
			InitializeConfigurationDataModelObject();						//Set the configuration table partition and row
			InitializeInstanceController();									//Sets the Instance Controller tables in your application's table storage space
			LoadConfigurationDataModelWithAppSettings();					//Creates the configuration if needed.

#endif
		}

		/// <summary>
		/// Setup CloudStorageAccount Configuration Setting Publisher - 
		/// This code sets up a handler to update CloudStorageAccount instances when their corresponding
		/// configuration settings change in the service configuration file.
		/// </summary>
		private static void ConfigureTableStorageAccess()
		{
			// This code sets up a handler to update CloudStorageAccount instances when their corresponding
			// configuration settings change in the service configuration file.
			CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) =>
			{
				// Provide the configSetter with the initial value
				configSetter(RoleEnvironment.GetConfigurationSettingValue(configName));
				RoleEnvironment.Changed += (sender1, arg) =>
				{
					if (arg.Changes.OfType<RoleEnvironmentConfigurationSettingChange>().Any(change =>
						(change.ConfigurationSettingName == configName)))
					{
						// The corresponding configuration setting has changed, propagate the value
						if (!configSetter(RoleEnvironment.GetConfigurationSettingValue(configName)))
						{
							// In this case, the change to the storage account credentials in the
							// service configuration is significant enough that the role needs to be
							// recycled in order to use the latest settings. (for example, the 
							// endpoint has changed)
							RoleEnvironment.RequestRecycle();
						}
					}
				};

			});
		}

		/// <summary>
		/// Setups the application table storage.
		/// </summary>
		private static void SetupApplicationTableStorage()
		{
			// For production code see discussion in AzureTableInit.cs
			// If you hard-code the suggested properties then the following code can be safely removed.
			// Optionally you could hard-code the table names and leave DataConnectionString more
			// dynamic in case you change your Azure account settings for any reason. In that case
			// you would leave all the following code, the table name will simply be replaced with the
			// hard-code version.

			//Initialize settings in your Azure Table Storage objects. You should have initialization for your
			//DataConnectionString and for each table name that you have created.
			//YOURNAMESPACE.CLASSNAME.AzureTableInitialization.SetAzureTableContextDefaults(
			//    Properties.Settings.Default.AzureDataConnectionString, Properties.Settings.Default.YOURDEFINITIONTableName);


			//This creates tables if needed.
			//if (
			//    !YOURNAMESPACE.CLASSNAME.AzureTableInitialization.InitializeTables(
			//        Properties.Settings.Default.AzureDataConnectionString, Properties.Settings.Default.YOURDEFINITIONTableName))
			//    throw new StorageClientException();
			// If you get an error here there is a problem with your DataConnectionString or with your
			// table name. Check that they exist and that there are not invalid characters in the names.
			// More detail can be gotten by setting breakpoints in the called method.

			// Additional table initializations here.
		}

#if (USEINSTANCECONTROL)

		#region Instance Controller interface code

		/// <summary>
		/// Initializes the configuration data model object.
		/// This is duplicated in the workerrole.cs
		/// </summary>
		public static void InitializeConfigurationDataModelObject()
		{
			int x = 0;
			while (!RoleEnvironment.IsAvailable || x == 100000)
			{
				x++;
			}
			ConfigurationDataModel = new InstanceController.ConfigurationDAL.ConfigurationDataModel(
				_appSettings.ConfigurationPartitionKey, _appSettings.ConfigurationRoleRowKey)
			{
				DeploymentID = RoleEnvironment.DeploymentId,
				RoleName = RoleEnvironment.CurrentRoleInstance.Role.Name
			};
		}

		/// <summary>
		/// Loads the configuration data model with _appSettings.
		/// </summary>
		public static void LoadConfigurationDataModelWithAppSettings()
		{
			// If the read finds nothing it returns null.
			ConfigurationDataModel =
				InstanceController.ConfigurationDAL.ConfigurationDataMethods.ReadRow(ConfigurationDataModel.PartitionKey,
																					 ConfigurationDataModel.RowKey);
			// If the Global.ConfigurationDataModel is null then create it.
			if (ConfigurationDataModel != null) return;
			ConfigurationDataModel = new InstanceController.ConfigurationDAL.ConfigurationDataModel(
				_appSettings.ConfigurationPartitionKey, _appSettings.ConfigurationRoleRowKey)
			{
				DeploymentID = RoleEnvironment.DeploymentId,
				RoleName = RoleEnvironment.CurrentRoleInstance.Role.Name,
				CertificateThumbPrint = _appSettings.ThumbPrint,
				SubscriptionID = _appSettings.SubscriptionID,
				MaxInstances = _appSettings.MaxInstances,
				MinInstances = _appSettings.MinInstances,
				IsActive = false
			};
			InstanceController.ConfigurationDAL.ConfigurationDataMethods.UpdateConfiguration(ConfigurationDataModel, true);
		}

		/// <summary>
		/// Initializes the instance controller. Hooks up or creates the diagnositics and management
		/// table storage.
		/// </summary>
		private static void InitializeInstanceController()
		{
			#region Start Table Initialization Section

			// *********************************************************************************************************
			// **** Table Storage Exceptions are almost always related to missing configuration connection strings. ****
			// **** This can be confusing.  In our configuration Application Settings there are several connection  ****
			// **** string definitions that all have the same value "TableStorageConnectionString". You only need   ****
			// **** a single entry in your WebRole properties that takes the value as the key and its value as the  ****
			// **** actual Azure connection. The reason for this is that it allows flexibility in the future for    ****
			// **** should the need arise to split the service across multiple storage accounts.                    ****
			// *********************************************************************************************************

			// For production code see discussion in AzureTableInit.cs
			// If you hard-code the suggested properties then the following code can be safely removed.
			// Optionally you could hard-code the table names and leave DataConnectionString more
			// dynamic in case you change your Azure account settings for any reason. In that case
			// you would leave all the following code, the table name will simply be replaced with the
			// hard-code version.

			//Initialize settings in your Azure Table Storage objects. You should have initialization for your
			//DataConnectionString and for each table name that you have created.
			InstanceController.ConfigurationDAL.AzureTableInitialization.SetAzureTableContextDefaults(
				_appSettings.AzureDataConnectionString, _appSettings.ConfigurationDALTableName);


			//This creates tables if needed.
			if (!InstanceController.ConfigurationDAL.AzureTableInitialization.InitializeTables(
					_appSettings.AzureDataConnectionString, _appSettings.ConfigurationDALTableName))
				throw new StorageClientException();
			// If you get an error here there is a problem with your DataConnectionString or with your
			// table name. Check that they exist in the webrole properties and that there are not invalid 
			// characters in the names. More detail can be gotten by setting breakpoints in the called method.

			// Additional table initializations here.
			InstanceController.StateMachineDAL.AzureTableInitialization.SetAzureTableContextDefaults(
				_appSettings.StateMachineConnectionString, _appSettings.StateMachineTableName);
			if (
				!InstanceController.StateMachineDAL.AzureTableInitialization.InitializeTables(
					_appSettings.StateMachineConnectionString, _appSettings.StateMachineTableName))
				throw new StorageClientException();

			InstanceController.RolesPerfMonDAL.AzureTableInitialization.SetAzureTableContextDefaults(
				_appSettings.PerformanceCountersConnectionString, _appSettings.CounterSettingsTableName);
			if (!InstanceController.RolesPerfMonDAL.AzureTableInitialization.InitializeTables(_appSettings.PerformanceCountersConnectionString, _appSettings.CounterSettingsTableName))
				throw new StorageClientException();

			InstanceController.WADPerformanceCountersDAL.AzureTableInitialization.SetAzureTableContextDefaults(
				_appSettings.WADiagnosticConnectionString, _appSettings.WADPerformanceCountersDALTableName);
			if (!InstanceController.WADPerformanceCountersDAL.AzureTableInitialization.InitializeTables(_appSettings.WADiagnosticConnectionString, _appSettings.WADPerformanceCountersDALTableName))
				throw new StorageClientException();


			#endregion End  Table Initialization Section
		}

		#endregion

#endif

	}
}
